import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

public class CommonUtil {
    /**
     * 方位角偏差修正
     */
    private static double[] rate = {-189, -218, -246, -273, -301, -327, -354, -380, -405, -430, -455, -479, -502, -525,
            -547, -568, -589, -609, -628, -647, -665, -682, -698, -714, -728, -742, -755, -779, -790, -799, -817, -824,
            -830, -836, -841, -845, -849, -851, -853, -854, -855, -854, -853, -851, -848, -845, -841, -836, -831, -824,
            -818, -810, -802, -793, -664, -774, -763, -752, -741, -728, -716, -703, -689, -675, -661, -647, -632, -616,
            -601, -585, -568, -552, -535, -518, -501, -484, -466, -449, -431, -413, -395, -377, -358, -340, -322, -304,
            -285, -267, -249, -231, -213, -196, -178, -161, -144, -127, -110, -93, -77, -61, 46, 30, 16, 1, 13, 27, 41,
            54, 66, 79, 91, 102, 113, 124, 134, 143, 153, 161, 169, 177, 184, 70, 196, 201, 206, 210, 217, 216, 219,
            220, 222, 222, 222, 222, 221, 219, 217, 214, 211, 207, 203, 198, 193, 187, 181, 174, 167, 159, 151, 142,
            133, 124, 114, 104, 94, 83, 72, 60, 48, 36, 24, 12, 1, 14, 39, 52, -65, -78, -91, -105, -117, -130, -143,
            -156, -168, -181, -193, -205, -217, -229, -240, -251, -262, -273, -283, -293, -302, -311, -320, -328, -336,
            -343, -350, -356, -362, -368, -372, -376, -380, -383, -385, -387, -389, -389, -389, -389, -388, -386, -384,
            -381, -377, -373, -368, -363, -357, -351, -344, -336, -328, -319, -310, -300, -290, -279, -267, -255, -242,
            -229, -216, -201, -187, -171, -156, -140, -123, -107, -89, -72, 54, 35, 17, 2, 21, 41, 60, 80, 100, 121,
            141, 162, 183, 183, 204, 225, 246, 267, 288, 310, 331, 353, 374, 395, 417, 438, 459, 480, 501, 522, 542,
            562, 582, 602, 621, 640, 659, 678, 696, 696, 713, 731, 748, 764, 780, 796, 796, 811, 825, 839, 853, 866,
            878, 890, 901, 912, 681, 931, 940, 948, 955, 961, 967, 972, 976, 980, 982, 984, 985, 985, 984, 983, 981,
            977, 973, 969, 963, 956, 949, 941, 932, 922, 911, 900, 887, 874, 860, 846, 830, 814, 797, 779, 760, 741,
            721, 700, 678, 656, 633, 609, 585, 561, 535, 509, 483, 456, 429, 402, 374, 346, 317, 288, 259, 230, 201,
            171, 142, 112, 82, 52, 23, 7, 37, -66, -96, -125, -154, -183};
    /**
     * 构建投影矩阵
     *
     * @param plane 目标平面方程
     * @param light 光线方向向量
     */
    public static double[][] buildProjectionMat(VectorVO plane, VectorVO light) {
        //目标平面方程
        Double a = plane.getA();
        Double b = plane.getB();
        Double c = plane.getC();
        Double d = plane.getD();
        //光线方向向量
        Double x = light.getA();
        Double y = light.getB();
        Double z = light.getC();
        double v = a * x + b * y + c * z;
        double[] li1 = {v - a * x, -1 * b * x, -1 * c * x, -1 * d * x};
        double[] li2 = {-1 * a * y, v - b * y, -1 * c * y, -1 * d * y};
        double[] li3 = {-1 * a * z, -1 * b * z, v - c * z, -1 * d * z};
        double[] li4 = {0, 0, 0, v};
        double[][] sum = {li1, li2, li3, li4};
        return sum;
    }

    /**
     * 集合转换成2维数组
     * @param lists
     * @return
     */
    public static double[][] changeListToArrays1(List<double[]> lists) {
        double[][] array = new double[lists.size()][];
        for (int i = 0; i < lists.size(); i++) {
            double[] row = lists.get(i);
            array[i] = new double[row.length];
            for (int j = 0; j < row.length; j++) {
                array[i][j] = row[j];
            }
        }
        return array;
    }

    /**
     * 矩阵翻转
     * @param matrix
     * @return
     */
    public  static double[][] transposeMatrix(double[][] matrix) {
        double[][] transposedMatrix = new double[matrix[0].length][matrix.length];
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[0].length; j++) {
                transposedMatrix[j][i] = matrix[i][j];
            }
        }
        return transposedMatrix;
    }

    /**
     * 两个向量的乘积
     * @param mat
     * @param arrays
     * @return
     */
    public static double[][] dot(double[][] mat,double[][] arrays){

        double[][] result = new double[mat.length][arrays[0].length];
        for (int i = 0; i < mat.length; i++) {
            for (int j = 0; j < arrays[0].length; j++) {
                for (int k = 0; k < mat[0].length; k++) {
                    result[i][j] += mat[i][k] * arrays[k][j];
                }
            }
        }
        return result;
    }

    public  static double[][] suo(double[][] matrix) {
        double[][] transposedMatrix = new double[matrix.length][matrix[0].length];
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[0].length; j++) {
                transposedMatrix[i][j] = matrix[i][j]/matrix[i][3];
            }
        }
        return transposedMatrix;
    }

    /**
     * 获取太阳高度角 和方位角
     * 计算公式：sinHs=cost*cosδ*cosΦ+sinδ*sinΦ
     * Hs 是太阳高度角
     * t 是以地方恒星时系统下的时角
     * δ 是目前的太阳赤纬
     * Φ 是当地的纬度
     */
    public static AngleVO getAspect(String time, double latitude ) throws ParseException {
        AngleVO anglePo = new AngleVO();
        Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(time);
        GregorianCalendar gc = (GregorianCalendar) Calendar.getInstance();
        gc.setTime(date);
        // 日序数 (例: 1月1日 n=1)
        int d = gc.get(Calendar.DAY_OF_YEAR);
        int year = gc.get(Calendar.YEAR);
        // 时序数 表示每天的时刻 如9:00表示9
        int h = gc.get(Calendar.HOUR_OF_DAY);
        int m = gc.get(Calendar.MINUTE);
        double v = MathUtil.changePoint(m / 60.0);
        // 赤纬角 弧度值
        double decAngleR = 0.006918
                - 0.399912 * MathUtil.cos(360 * (d - 1) / 365d)
                + 0.070257 * MathUtil.sin(360 * (d - 1) / 365d)
                - 0.006758 * MathUtil.cos(2 * (360 * (d - 1) / 365d))
                + 0.000907 * MathUtil.sin(2 * (360 * (d - 1) / 365d))
                - 0.002697 * MathUtil.cos(3 * (360 * (d - 1) / 365d))
                + 0.00148 * MathUtil.sin(3 * (360 * (d - 1) / 365d));

        // 时角 角度值
        double pian = getPian(d);
        if(!isGregorianLeapYear(year)){
            //非闰年
            if(d>59){
                pian=getPian(d+1);
            }
        }
        isGregorianLeapYear(year);
        double t = (h+v - 12+pian) * 15;
        // 太阳高度角 角度值
        double altitude = MathUtil.asin(MathUtil.sin(latitude) * Math.sin(decAngleR) + MathUtil.cos(latitude) * Math.cos(decAngleR) * MathUtil.cos(t));
        // 太阳方位角 角度值
        double azimuth =(MathUtil.sin(latitude) * MathUtil.sin(altitude)-Math.sin(decAngleR)) / (MathUtil.cos(altitude)*MathUtil.cos(latitude));
        azimuth = MathUtil.acos(azimuth);
        double flag=-1.0;
        if(t>0){
            flag=1;
        }
        //方位角需要加上180
        azimuth=azimuth*flag+180;
        anglePo.setAltitude(altitude);
        anglePo.setAzimuth(azimuth);
        return anglePo;
    }

    /**
     * 基于太阳高度角和方位角，计算太阳光线方向向量
     *
     * @param altitude 太阳高度角
     * @param azimuth  太阳方位角
     * @return
     */
    public static VectorVO getVectorByDirection(double altitude, double azimuth) {
        VectorVO vectorPo = new VectorVO();
        double newAzimuth = azimuth;
        Double a = 0D;
        Double b = 0D;
        Double c = 0D;
        //高度角不等于0
        if (altitude > 1e-6) {
            //方位角等于0 或2pi
            if (MathUtil.abs(newAzimuth) < 1e-6 || MathUtil.abs(newAzimuth - 2 * 180) < 1e-6) {
                b = 1.0;
            } else if (MathUtil.abs(newAzimuth - 180) < 1e-6) {
                // 方位角等于pi
                b = -1.0;
            } else {
                if (newAzimuth < 180) {
                    a = 1.0;
                } else {
                    a = -1.0;
                }
                b = a / MathUtil.tan(newAzimuth);
                c = Math.sqrt(a * a + b * b) * MathUtil.tan(altitude);
            }

        } else {
            c = 1.0;
        }
        if (c < 0) {
            vectorPo.setA(a);
            vectorPo.setB(b);
            vectorPo.setC(c);
            vectorPo.setD(0D);
            return vectorPo;
        }
        vectorPo.setA(-1 * a);
        vectorPo.setB(-1 * b);
        vectorPo.setC(-1 * c);
        vectorPo.setD(0D);
        return vectorPo;
    }

    /**
     * 用于判断输入的年份是否为闰年
     *
     * @param year
     *            输入的年份
     * @return true 表示闰年
     */
    public static boolean isGregorianLeapYear(int year) {
        boolean isLeap = false;
        if (year % 4 == 0)
            isLeap = true;
        if (year % 100 == 0)
            isLeap = false;
        if (year % 400 == 0)
            isLeap = true;
        return isLeap;
    }

    public static double getPian(int d){
        double v = rate[d -1];
        return v/3600d;
    }

    /**
     * 计算向量极角（与水平线夹角）
     * @param p1
     * @return
     */
    public static double calculatePolarAngle(Vector2VO p1){

        return Math.atan2(p1.getY(),p1.getX());
    }

    /**
     * 二维向量
     * @param p1
     * @param p2
     * @return
     */
    public static Vector2VO vector2d(PointVO p1,PointVO p2){
        double x = p2.x - p1.x;
        double y = p2.y - p1.y;
        Double aDouble = euclideanDistance(p1, p2);
        return new Vector2VO(x,y,aDouble);
    }

    /**
     * 计算两点欧式距离
     * @param p1
     * @param p2
     * @return
     */
    public static Double euclideanDistance(PointVO p1, PointVO p2){
        return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
    }

    //map 排序 从大到小
    public static <K extends Comparable,V extends Comparable> Map<K, V> sortDescMapByValues(Map<K, V> map){
        //需要用LinkedHashMap排序
        HashMap<K, V> finalMap = new LinkedHashMap<K, V>();
        //取出map键值对Entry<K,V>，然后按照值排序，最后组成一个新的列表集合
        List<Map.Entry<K, V>> list = map.entrySet()
                .stream()
                //sorted((p2,p1)   表示由大到小排序   ||  sorted((p1,p2)   表示由小到大排序
                .sorted((p1,p2)->p1.getValue().compareTo(p2.getValue()))
                .collect(Collectors.toList());
        //遍历集合，将排好序的键值对Entry<K,V>放入新的map并返回。
        list.forEach(ele->finalMap.put(ele.getKey(), ele.getValue()));
        return finalMap;
    }

    /**
     * 计算向量A沿顺时针方向旋转到向量B的角度（0-2π）
     * @param p1
     * @param p2
     * @return
     */
    public static double calculateVectorAngle(Vector2VO p1, Vector2VO p2){
        double theta = Math.acos(dotProduct2(p1, p2)/(p1.getDist()*p2.getDist()));
        double crossV = crossProduct2(p1, p2);
        double angle=0d;
        if(crossV>0){
            angle=2*Math.PI-theta;
        }else {
            angle=theta;
        }
        return angle;
    }

    /**
     * 向量点乘
     * @param p1
     * @param p2
     */
    public static double dotProduct2(Vector2VO p1,Vector2VO p2){
        return p1.getX()*p2.getX()+p1.getY()*p2.getY();
    }

    /**
     * 向量叉乘
     * @param p1
     * @param p2
     */
    public static double crossProduct2(Vector2VO p1,Vector2VO p2){
        return p1.getX()*p2.getY()-p1.getY()*p2.getX();
    }

    /**
     * 三点点坐标确定平面方程
     * @param points
     * @return
     */
    public static VectorVO planeEquation(List<PointVO> points) {
        VectorVO vectorPo = new VectorVO();
        if (points.size() > 2) {
            List<PointVO> points1 = points.subList(0, 3);
            PointVO point = points1.get(0);
            PointVO point1 = points1.get(1);
            PointVO point2 = points1.get(2);
            // 已知三个点的坐标
            double x1 = point.getX();
            double y1 = point.getY();
            double z1 = point.getZ();
            double x2 = point1.getX();
            double y2 = point1.getY();
            double z2 = point1.getZ();
            double x3 = point2.getX();
            double y3 = point2.getY();
            double z3 = point2.getZ();
            // 计算A、B、C、D
            double A =MathUtil.changePoint( y1 * (z2 - z3) + y2 * (z3 - z1) + y3 * (z1 - z2));
            double B =MathUtil.changePoint( z1 * (x2 - x3) + z2 * (x3 - x1) + z3 * (x1 - x2));
            double C =MathUtil.changePoint( x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2));
            double D = MathUtil.changePoint(-(x1 * (y2 * z3 - y3 * z2) + x2 * (y3 * z1 - y1 * z3) + x3 * (y1 * z2 - y2 * z1)));
            vectorPo.setA(A);
            vectorPo.setB(B);
            vectorPo.setC(C);
            vectorPo.setD(D);
            return vectorPo;
        }
        vectorPo.setA(0D);
        vectorPo.setB(0d);
        vectorPo.setC(1d);
        vectorPo.setD(0d);
        return vectorPo;
    }
}
